using DG.Tweening;
using System.Collections.Generic;
using UnityEngine;

namespace BToolkit
{
    [AddComponentMenu("BToolkit/LabaDriver")]
    public class LabaDriver : MonoBehaviour
    {
        public delegate void CellUpdateAction<T>(T cell, int index, bool isInit);
        public delegate void CellStopAction<T>(T cell, int index);
        class Cell
        {
            public int index;
            public Component component;
            public RectTransform trans;
            public float offsetY;
        }

        public float space = 10;
        public int roundCount = 5;
        public float accelDuration = 1;
        public float decelDuration = 3;
        public RectTransform cell;
        public bool isRunning { get; private set; }

        private int total;
        private float maskHeight, cellHeight;
        private List<Cell> cells = new List<Cell>();
        private List<Cell> cells2 = new List<Cell>();
        private CellUpdateAction<Cell> onCellUpdate;
        private CellStopAction<Cell> onStop;
        private int tweenId;

        private Cell GetBottomCell()
        {
            Cell bottomCell = cells[0];
            float minY = bottomCell.trans.anchoredPosition.y;
            for (int i = 0; i < cells.Count; i++)
            {
                Cell cell = cells[i];
                if (cell.trans.anchoredPosition.y < minY)
                {
                    minY = cell.trans.anchoredPosition.y;
                    bottomCell = cell;
                }
            }
            return bottomCell;
        }

        private Cell GetTopCell()
        {
            Cell topCell = cells[0];
            float maxY = topCell.trans.anchoredPosition.y;
            for (int i = 0; i < cells.Count; i++)
            {
                Cell cell = cells[i];
                if (cell.trans.anchoredPosition.y > maxY)
                {
                    maxY = cell.trans.anchoredPosition.y;
                    topCell = cell;
                }
            }
            return topCell;
        }

        private Cell GetOverCenterFirstCell()
        {
            for (int i = 0; i < cells.Count; i++)
            {
                Cell cell = cells2[i];
                if (cell.trans.anchoredPosition.y >= 0)
                {
                    return cell;
                }
            }
            return null;
        }

        private Cell GetCellByIndex(int index)
        {
            for (int i = 0; i < cells.Count; i++)
            {
                Cell cell = cells[i];
                float y = cell.trans.anchoredPosition.y;
                float top = maskHeight * 0.5f;
                float bottom = -maskHeight * 0.5f;
                if (cell.index == index && y < top && y > bottom)
                {
                    return cell;
                }
            }
            return null;
        }

        private void OnDestroy()
        {
            DOTween.Kill(tweenId);
        }


        #region 对外方法///////////////////////////////////////////////////////////////////////

        public void SetCellUpdateCallback<T>(int total, CellUpdateAction<T> onCellUpdate) where T : Component
        {
            this.total = total;

            this.onCellUpdate = (Cell cell, int index, bool isInit) =>
            {
                onCellUpdate?.Invoke(cell.component as T, index, isInit);
            };

            this.maskHeight = (cell.parent as RectTransform).rect.height;
            this.cellHeight = cell.rect.height;
            int spawnCount = Mathf.CeilToInt(maskHeight / (this.cellHeight + this.space)) + 1;
            for (int i = 0; i < spawnCount; i++)
            {
                RectTransform trans = null;
                if (i == 0)
                {
                    trans = this.cell;
                }
                else
                {
                    trans = Instantiate(this.cell, this.cell.parent, false);
                }
                trans.gameObject.SetActive(true);
                Cell cell = new Cell();
                cell.trans = trans;
                cell.trans.name = $"Cell_{i}";
                this.cells.Add(cell);
                this.cells2.Add(cell);
            }

            for (int i = 0; i < cells.Count; i++)
            {
                int index = cells.Count - i - 1;
                Cell _cell = cells[index];
                _cell.component = _cell.trans.GetComponent<T>();
                _cell.index = index;
                float y = maskHeight * 0.5f - space - cellHeight * 0.5f - i * (cellHeight + space);
                _cell.trans.anchoredPosition = new Vector2(0, y);
                while (_cell.index > total - 1)
                {
                    _cell.index = _cell.index - total;
                }
                this.onCellUpdate?.Invoke(_cell, _cell.index, true);
            }
        }

        public void StartRun<T>(int stopIndex, CellStopAction<T> onStop) where T : Component
        {
            if (stopIndex < 0 || stopIndex > total - 1)
            {
                Debug.LogError("stopIndex超出有效范围");
                return;
            }

            if (isRunning)
            {
                return;
            }

            this.onStop = (Cell cell, int index) =>
            {
                onStop?.Invoke(cell.component as T, index);
            };

            //初始化
            Cell bottomCell = GetBottomCell();
            float bottomCellY = bottomCell.trans.anchoredPosition.y;
            cells2.Sort((Cell A, Cell B) =>
            {
                return A.trans.anchoredPosition.y - B.trans.anchoredPosition.y > 0 ? 1 : -1;
            });
            for (int i = 0; i < cells.Count; i++)
            {
                Cell cell = cells2[i];
                cell.offsetY = bottomCellY + i * (cellHeight + space);
            }

            float fromY = 0, toY = 0;
            for (int i = 0; i < roundCount; i++)
            {
                float oneRoundDis = (cellHeight + space) * total;
                toY -= oneRoundDis;
            }

            Cell centerCell = GetOverCenterFirstCell();
            int index = centerCell.index;
            float offfsetY = centerCell.trans.anchoredPosition.y;
            for (int i = 0; i < total; i++)
            {
                if (index == stopIndex)
                {
                    toY -= offfsetY;
                    break;
                }
                offfsetY += cellHeight + space;
                index += 1;
                if (index > total - 1)
                {
                    index = 0;
                }
            }

            //开始移动
            int topCellIndex = GetTopCell().index;
            float allCellsHeight = (cellHeight + space) * cells.Count;
            isRunning = true;
            float middleY = fromY + (toY - fromY) * 0.3f;
            DOVirtual.Float(fromY, middleY, accelDuration, (float value) =>
            {
                //加速
                UpdateValue(allCellsHeight, ref topCellIndex, value);

            }).SetEase(Ease.InSine).SetId(tweenId).OnComplete(() =>
            {
                DOVirtual.Float(middleY, toY, decelDuration, (value) =>
                {
                    //减速
                    UpdateValue(allCellsHeight, ref topCellIndex, value);
                }).SetEase(Ease.OutSine).OnComplete(() =>
                {
                    isRunning = false;
                    Cell resultCell = GetCellByIndex(stopIndex);
                    this.onStop?.Invoke(resultCell, stopIndex);
                }).SetId(tweenId);
            });
        }

        private void UpdateValue(float allCellsHeight, ref int topCellIndex, float value)
        {
            for (int i = 0; i < cells.Count; i++)
            {
                Cell cell = cells2[i];
                float y = value + cell.offsetY;
                if (y < -maskHeight * 0.5f - cellHeight)
                {
                    cell.offsetY = cell.offsetY + allCellsHeight;
                    y = value + cell.offsetY;
                    topCellIndex = topCellIndex + 1;
                    if (topCellIndex > total - 1)
                    {
                        topCellIndex = 0;
                    }
                    cell.index = topCellIndex;
                    this.onCellUpdate?.Invoke(cell, cell.index, false);
                }
                cell.trans.anchoredPosition = new Vector2(0, y);
            }
        }

        /// <summary>
        /// 立即停在
        /// </summary>
        public void Stop()
        {
            DOTween.Kill(tweenId);
        }

        #endregion

    }
}